{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# メソッドの可視化\n",
    "\n",
    "* publicが指定されたメソッド\n",
    "    * どのインスタンスからも実行できる\n",
    "    * デフォルト\n",
    "* protectedが指定されたメソッド\n",
    "    * 自分自身は実行できる\n",
    "    * サブクラスのインスタンスから実行できる\n",
    "* privateが指定されたメソッド\n",
    "    * <font color=\"red\">レシーバをつけた呼び出しはできない</font>\n",
    "    * [参考] Javaの場合\n",
    "        * クラス内のみアクセス可能\n",
    "        \n",
    "        \n",
    "protectedとprivateの違いはそのレシーバを自クラスかサブクラスのレシーバーが利用するかどうかの違い\n",
    "\n",
    "[Ruby の private と protected 。歴史と使い分け](http://qiita.com/tbpgr/items/6f1c0c7b77218f74c63e)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "### publicメソッドの実行\n",
      "public method1\n",
      "public method2\n",
      "\n",
      "### 自分自身(Foo)のメソッド(methodA)からprotectedが指定されたメソッド(protected_method1)を実行\n",
      "protected method1\n",
      "### サブクラス(FooExt)のメソッド(methodB)からprotectedが指定されたメソッド(protected_method2)を実行\n",
      "protected method2\n",
      "\n",
      "### 自分自身(Foo)のメソッド(methodC)からprivateが指定されたメソッド(private_method1)を実行\n",
      "private method1\n",
      "### サブクラス(FooExt)のメソッド(methodD)からprivateが指定されたメソッド(private_method2)を実行\n",
      "private method2\n"
     ]
    }
   ],
   "source": [
    "class Foo\n",
    "  def public_method1\n",
    "    \"public method1\"\n",
    "  end\n",
    "\n",
    "  ### デフォルトがpublicなので何も変わらない\n",
    "  public\n",
    "\n",
    "  def public_method2\n",
    "    \"public method2\"\n",
    "  end\n",
    "  \n",
    "  def methodA\n",
    "    protected_method1\n",
    "  end\n",
    "\n",
    "  def methodC\n",
    "    private_method1\n",
    "  end\n",
    "\n",
    "  protected\n",
    "\n",
    "  def protected_method1\n",
    "    \"protected method1\"\n",
    "  end\n",
    "  def protected_method2\n",
    "    \"protected method2\"\n",
    "  end\n",
    "  \n",
    "  private\n",
    "\n",
    "  def private_method1\n",
    "    \"private method1\"\n",
    "  end\n",
    "  def private_method2\n",
    "    \"private method2\"\n",
    "  end\n",
    "end\n",
    "\n",
    "class FooExt < Foo\n",
    "  def methodB\n",
    "    protected_method2\n",
    "  end\n",
    "\n",
    "  def methodD\n",
    "    private_method2\n",
    "  end\n",
    "end\n",
    "\n",
    "puts \"### publicメソッドの実行\"\n",
    "puts Foo.new.public_method1\n",
    "puts Foo.new.public_method2\n",
    "puts \"\"\n",
    "\n",
    "puts \"### 自分自身(Foo)のメソッド(methodA)からprotectedが指定されたメソッド(protected_method1)を実行\"\n",
    "puts Foo.new.methodA\n",
    "puts \"### サブクラス(FooExt)のメソッド(methodB)からprotectedが指定されたメソッド(protected_method2)を実行\"\n",
    "puts FooExt.new.methodB\n",
    "### メソッドを介せず直接実行するとNoMethodError\n",
    "# puts Foo.new.protected_method1\n",
    "puts \"\"\n",
    "\n",
    "puts \"### 自分自身(Foo)のメソッド(methodC)からprivateが指定されたメソッド(private_method1)を実行\"\n",
    "puts Foo.new.methodC\n",
    "puts \"### サブクラス(FooExt)のメソッド(methodD)からprivateが指定されたメソッド(private_method2)を実行\"\n",
    "puts FooExt.new.methodD\n",
    "### メソッドを介せず直接実行するとNoMethodError\n",
    "#puts Foo.new.private_method1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## 引数を指定した呼び出し"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Foo2"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class Foo2\n",
    "  def public_method1; 1; end\n",
    "  def public_method2; 2; end\n",
    "  def protected_method1; 1; end\n",
    "  def protected_method2; 2; end\n",
    "  def private_method1; 1; end\n",
    "  \n",
    "  ### メソッド名をシンボルで渡して後で指定\n",
    "  public :public_method1, :public_method2\n",
    "  protected :protected_method1, :protected_method2\n",
    "  private :private_method1\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## privateの振る舞い\n",
    "\n",
    "* selfなどレシーバを指定するとエラーになる\n",
    "    * protectedはならない"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "private method\n"
     ]
    }
   ],
   "source": [
    "### privateメソッドの挙動\n",
    "class Foo3\n",
    "  def public_method1\n",
    "    private_method1\n",
    "  end\n",
    "  \n",
    "  def public_method2\n",
    "    self.private_method1\n",
    "  end\n",
    "\n",
    "  private\n",
    "  \n",
    "  def private_method1; \"private method\"; end\n",
    "end\n",
    "\n",
    "puts Foo3.new.public_method1\n",
    "### selfなどレシーバを指定するとエラーとなる\n",
    "#Foo3.new.public_method2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "protected method\n",
      "protected method\n"
     ]
    }
   ],
   "source": [
    "### protectedメソッドの挙動\n",
    "class Foo4\n",
    "  def public_method1\n",
    "    protected_method1\n",
    "  end\n",
    "  \n",
    "  def public_method2\n",
    "    self.protected_method1\n",
    "  end\n",
    "\n",
    "  protected\n",
    "  \n",
    "  def protected_method1; \"protected method\"; end\n",
    "end\n",
    "\n",
    "puts Foo4.new.public_method1\n",
    "### selfなどレシーバを指定してもエラーにならない\n",
    "puts Foo4.new.public_method2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "protected method\n",
      "private method `private_method' called for #<Foo5:0x007f9f9d9fcb00>\n"
     ]
    }
   ],
   "source": [
    "class Foo5\n",
    "  def use_protected_method(other_obj)\n",
    "    other_obj.protected_method\n",
    "  end\n",
    "\n",
    "  def use_private_method(other_obj)\n",
    "    other_obj.private_method   ### privateメソッドはレシーバを明示的に指定できないのでエラーになる\n",
    "  end\n",
    "\n",
    "  protected\n",
    "  \n",
    "  def protected_method; \"protected method\"; end\n",
    "\n",
    "  private\n",
    "  \n",
    "  def private_method; \"private method\"; end\n",
    "end\n",
    "\n",
    "x = Foo5.new\n",
    "y = Foo5.new\n",
    "\n",
    "puts x.use_protected_method(y)\n",
    "\n",
    "begin\n",
    "  puts x.use_private_method(y)  ### オブジェクトxは同じクラスのオブジェクトyのプライベートメソッドを呼び出せない\n",
    "rescue => ex\n",
    "  puts ex\n",
    "end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "ename": "NoMethodError",
     "evalue": "private method `private_method' called for #<Foo2:0x007f9f9c9ba580>",
     "output_type": "error",
     "traceback": [
      "\u001b[31mNoMethodError\u001b[0m: private method `private_method' called for #<Foo2:0x007f9f9c9ba580>",
      "\u001b[37m(pry):251:in `use_private_method'\u001b[0m",
      "\u001b[37m(pry):262:in `<main>'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:355:in `eval'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:355:in `evaluate_ruby'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:323:in `handle_line'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:243:in `block (2 levels) in eval'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:242:in `catch'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:242:in `block in eval'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:241:in `catch'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/pry-0.10.4/lib/pry/pry_instance.rb:241:in `eval'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/backend.rb:66:in `eval'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/backend.rb:12:in `eval'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/kernel.rb:87:in `execute_request'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/kernel.rb:47:in `dispatch'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/kernel.rb:37:in `run'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/command.rb:70:in `run_kernel'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/lib/iruby/command.rb:34:in `run'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/gems/iruby-0.3/bin/iruby:5:in `<top (required)>'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/bin/iruby:23:in `load'\u001b[0m",
      "\u001b[37m/Users/ftakao2007/work/jupyter/vendor/bundle/ruby/2.3.0/bin/iruby:23:in `<main>'\u001b[0m"
     ]
    }
   ],
   "source": [
    "class Foo2\n",
    "  def use_private_method(other_obj)\n",
    "    other_obj.private_method\n",
    "  end\n",
    "\n",
    "  private\n",
    "  \n",
    "  def private_method; \"private method\"; end\n",
    "end\n",
    "\n",
    "x = Foo2.new\n",
    "y = Foo2.new\n",
    "\n",
    "putes x.use_private_method(y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## サブクラスで可視化の変更"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"private method1\""
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class Foo\n",
    "  private \n",
    "  def private_method1\n",
    "    \"private method1\"\n",
    "  end\n",
    "end\n",
    "\n",
    "class FooExt < Foo\n",
    "  ### publicに可視化を変更\n",
    "  public :private_method1\n",
    "end\n",
    "\n",
    "### privateメソッドは直接実行できない\n",
    "#Foo.new.private_method1\n",
    "\n",
    "### サブクラスで可視化をpublicに変更しているので直接実行できる\n",
    "FooExt.new.private_method1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# Kernelモジュールと関数\n",
    "\n",
    "* Kernelモジュールのメソッド\n",
    "    * puts\n",
    "    * proc\n",
    "    * その他\n",
    "* Rubyは本当の意味での関数は存在しない\n",
    "    * 関数に見えるものはすべてメソッド"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "## Kernelモジュールの呼ばれ方\n",
    "\n",
    "* トップレベルのクラスはObject\n",
    "* <font color=\"red\">ObjectクラスはKernelモジュールをincludeしている</font>\n",
    "    * 最終的にKernelモジュールで定義されたメソッドが呼び出される\n",
    "* Objectクラスは全てのクラスのスーパークラス\n",
    "    * Kernelモジュールに定義したメソッドはプログラム中のどこからでも呼び出すことができる\n",
    "    * Kernelモジュールの多くのメソッドはprivateが指定されているのでレシーバを指定して呼び出すことはできない\n",
    "    * Rubyの組み込み関数と同等のものを定義したい場合は、Kernelモジュールにprivateメソッドを定義すればよい\n",
    "        * ただし、<font color=\"red\">特に理由がない限りはKernelモジュールよりもObjectクラスにprivateメソッドを定義することが推奨される</font>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[:pry, :__binding__, :pretty_print, :pretty_print_cycle, :pretty_print_instance_variables, :pretty_print_inspect, :to_json, :instance_of?, :public_send, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :remove_instance_variable, :private_methods, :kind_of?, :instance_variables, :tap, :public_method, :singleton_method, :is_a?, :extend, :define_singleton_method, :method, :to_enum, :enum_for, :<=>, :===, :=~, :!~, :eql?, :respond_to?, :freeze, :inspect, :display, :object_id, :send, :gem, :to_s, :pretty_inspect, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :trust, :untrusted?, :methods, :protected_methods, :frozen?, :public_methods, :singleton_methods, :!, :==, :!=, :__send__, :equal?, :instance_eval, :instance_exec, :__id__]\n",
      "Object\n",
      "\n",
      "[:initialize, :inherited, :attr_reader, :attr, :attr_accessor, :attr_writer, :remove_const, :undef_method, :method_added, :method_removed, :remove_method, :method_undefined, :alias_method, :public, :private, :initialize_copy, :initialize_clone, :protected, :included, :extended, :prepended, :define_method, :using, :Digest, :DelegateClass, :sprintf, :format, :Integer, :Float, :String, :Array, :Hash, :catch, :throw, :loop, :block_given?, :trace_var, :untrace_var, :Rational, :at_exit, :Complex, :set_trace_func, :select, :caller, :caller_locations, :test, :`, :fork, :exit, :jj, :sleep, :gem_original_require, :pp, :respond_to_missing?, :load, :syscall, :printf, :open, :putc, :print, :readline, :puts, :p, :exit!, :system, :readlines, :gets, :URI, :proc, :lambda, :exec, :rand, :srand, :initialize_dup, :j, :spawn, :abort, :trap, :JSON, :require, :require_relative, :binding, :Pathname, :local_variables, :warn, :raise, :fail, :global_variables, :__method__, :__callee__, :__dir__, :eval, :iterator?, :method_missing, :singleton_method_added, :singleton_method_removed, :singleton_method_undefined]\n"
     ]
    }
   ],
   "source": [
    "### トップレベルのクラスはObject\n",
    "puts Object.instance_methods\n",
    "puts self.class\n",
    "puts #\n",
    "\n",
    "### レシーバを指定してputsメソッドを呼ぶことはできない\n",
    "puts Object.private_methods\n",
    "#self.puts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n"
     ]
    }
   ],
   "source": [
    "### Kernelモジュールにprivateメソッドを追加\n",
    "module Kernel\n",
    "  private\n",
    "  def my_func; 1; end\n",
    "end\n",
    "\n",
    "puts my_func\n",
    "### Error\n",
    "#puts self.my_func\n",
    "\n",
    "### Objectクラスにprivateメソッドを追加すること推奨\n",
    "class Object\n",
    "  private\n",
    "  def my_func2; 2; end\n",
    "end\n",
    "\n",
    "puts my_func2\n",
    "### Error\n",
    "#puts self.my_func2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### selfについて\n",
    "\n",
    "* 4-4.SingletonClass.ipynb で記載"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Ruby 2.3.1",
   "language": "ruby",
   "name": "ruby"
  },
  "language_info": {
   "file_extension": ".rb",
   "mimetype": "application/x-ruby",
   "name": "ruby",
   "version": "2.3.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
